/* hal_scu.c */
#include "hal_scu.h"

void SCU_SetSysClkSrcMux(SCU_Type * base, SCU_ClkSrc_Type mux)
{
    base->CLKS = SCU_CLKS_CLKSRCSEL(mux);
}

void SCU_SetSysClkSrcDiv(SCU_Type * base, uint32_t core_clk_div, uint32_t fast_bus_clk_div, uint32_t slow_bus_clk_div)
{
    base->DIV = SCU_DIV_CPDIVS(core_clk_div)     /* div from system clock to core clock. */
              | SCU_DIV_FBDIVS(fast_bus_clk_div) /* div from system clock to fast bus clock. */
              | SCU_DIV_SBDIVS(slow_bus_clk_div) /* div from fast bus clock to slow bus clock. */
              ;
}

SCU_ClkSrc_Type SCU_GetSysClkSrcMux(SCU_Type * base)
{
    return (SCU_ClkSrc_Type)((base->STS) & SCU_STS_CLKST_MASK);
}

bool SCU_GetSysClkSrcValidOn(SCU_Type *base, uint32_t clks) /* SCU_STATUS_XXX_CLK_VALID. */
{
    return (clks == ((base->STS >> SCU_STS_FIRC_VALID_SHIFT) & clks));
}

void SCU_GetSysClkSrcDiv(SCU_Type * base, uint32_t *core_clk_div, uint32_t *fast_bus_clk_div, uint32_t *slow_bus_clk_div)
{
    uint32_t tmp32 = base->DIVSTS;
    *core_clk_div     = (tmp32 & SCU_DIVSTS_CPDIVST_MASK) >> SCU_DIVSTS_CPDIVST_SHIFT;
    *fast_bus_clk_div = (tmp32 & SCU_DIVSTS_FBDIVST_MASK) >> SCU_DIVSTS_FBDIVST_SHIFT;
    *slow_bus_clk_div = (tmp32 & SCU_DIVSTS_SBDIVST_MASK) >> SCU_DIVSTS_SBDIVST_SHIFT;
}

void SCU_SetFircConf(SCU_Type * base, SCU_FircConf_Type * conf)
{
    if (!conf)
    {
        base->FIRC_CTRL = 0u;
    }
    else
    {
        uint32_t tmp32 = SCU_FIRC_CTRL_FIRC_EN_MASK;
        
        if (conf->EnableOnDeepsleepMode)
        {
            tmp32 |= SCU_FIRC_CTRL_FIRC_DS_EN_MASK;
        }
        base->FIRC_CTRL = tmp32;
    }
}

/* SIRC is always enabled. */
void SCU_SetSircConf(SCU_Type * base, SCU_SircConf_Type * conf)
{
    if (!conf)
    {
        base->SIRC_CTRL = 0u; /* SIRC is always enabled. */
    }
    else
    {
        uint32_t tmp32 = (conf->EnableOnDeepsleepMode ? SCU_SIRC_CTRL_SIRC_DS_EN_MASK : 0u)
                       | (conf->EnableOnStandbyMode   ? SCU_SIRC_CTRL_SIRC_STB_EN_MASK: 0u)
                       ;
        base->SIRC_CTRL = tmp32;
    }
}

void SCU_SetFxoscConf(SCU_Type * base, SCU_FxoscConf_Type * conf)
{
    /* disable the fast osc before any setting. */
    base->FXOSC_CTRL = 0u;
    
    if (conf)
    {
        /* setup the fast osc working mode. */
        uint32_t tmp32 = (conf->EnableOnDeepsleepMode ? SCU_FXOSC_CTRL_FXOSC_DS_EN_MASK : 0u)
                       | (conf->EnableCrystalMode ? (SCU_FXOSC_CTRL_FXOSC_MODE_MASK ) : 0u)
                       | SCU_FXOSC_CTRL_FXOSC_GAIN_SEL(conf->GainVal)
                       ;
        base->FXOSC_CTRL = tmp32;
        
        /* enable the fast osc. */
        tmp32 |= SCU_FXOSC_CTRL_FXOSC_EN_MASK;
        base->FXOSC_CTRL = tmp32;
    }
}

void SCU_SetSxoscConf(SCU_Type * base, SCU_SxoscConf_Type * conf)
{
    /* disable the slow osc before any setting. */
    base->SXOSC_CTRL = 0u;
    
    if (conf)
    {
        /* setup the working mode. */
        uint32_t tmp32  = (conf->EnableOnStandbyMode ? SCU_SXOSC_CTRL_SXOSC_STB_EN_MASK : 0u)
                        | (conf->EnableOnDeepsleepMode ? SCU_SXOSC_CTRL_SXOSC_DS_EN_MASK : 0u)
                        | (conf->EnableCrystalMode ? SCU_SXOSC_CTRL_SXOSC_MODE_MASK : 0u)
                        | SCU_SXOSC_CTRL_SXOSC_HIGAIN_EN(conf->GainVal)
                        ;
        base->SXOSC_CTRL = tmp32;
        
        /* enable the slow osc. */
        tmp32 |= SCU_SXOSC_CTRL_SXOSC_EN_MASK;
        base->SXOSC_CTRL = tmp32;
        
    }
}

void SCU_SetClkOutSrcMux(SCU_Type *base, SCU_ClkOutSrc_Type clk_src, uint32_t clk_div)
{
    base->CLKO = SCU_CLKO_CLKOSEL(clk_src) | SCU_CLKO_CLKODIV(clk_div);
}

void SCU_SetCmuConf(SCU_Type * base, uint32_t cmu_idx, SCT_CmuConf_Type * conf)
{
    if (!conf)
    {
        base->CMU_CTRL &= ~((SCU_CMU_CTRL_CMU0_EN_MASK | SCU_CMU_CTRL_CMU0_RE_MASK ) << cmu_idx);
    }
    else
    {
        uint32_t tmp32  = ( (base->CMU_CTRL & ~SCU_CMU_CTRL_CMU0_RE_MASK ) << cmu_idx)
                        | (SCU_CMU_CTRL_CMU0_EN_MASK << cmu_idx)
                        | ((conf->EnableResetOnCmuEvent ? SCU_CMU_CTRL_CMU0_RE_MASK : 0u) << cmu_idx)
                        ;
        base->CMU_CTRL = tmp32;
        base->CMUCMP[cmu_idx].HIGH = conf->CmuHighVal;
        base->CMUCMP[cmu_idx].LOW  = conf->CmuLowVal;
    }
}



uint32_t SCU_GetCmuStatusFlags(SCU_Type * base, uint32_t cmu_idx)
{
    uint32_t tmp32 = base->CMUSTS;
    
    uint32_t flags = 0u;
    
    if (0u != (tmp32 & (SCU_CMUSTS_CMU0_OUTRNG_MASK << cmu_idx)))
    {
        flags |= SCU_CMU_STATUE_CLK_OUT_OF_RANGE;
    }
    if (0u != (tmp32 & (SCU_CMUSTS_CMU0_LOSR_MASK << cmu_idx)))
    {
        flags |= SCU_CMU_STATUE_LOSS_OF_REF_CLK;
    }
    if (0u != (tmp32 & (SCU_CMUSTS_CMU0_LOSC_MASK << cmu_idx)))
    {
        flags |= SCU_CMU_STATUE_LOSS_OF_CLK;
    }

    return flags;
}

/* EOF. */

